package com.crpc.core.spi;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.crpc.core.config.ObjectWrapper;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author cloud
 * @description: 实现简易的Spi
 * @date 2024/3/9 14:31
 */
@Slf4j
public class SpiHandler {
    //定义一个缓存，保存原始内容
    private static final Map<String, List<String>> SPI_CONTENT = new ConcurrentHashMap<>(8);
    private static final Map<Class<?>, List<ObjectWrapper<?>>> SPI_IMPLEMENT = new ConcurrentHashMap<>(32);

    private static final String BASE_PATH = "META-INF/CRPC-SERVICES";

    static {

        //  加载 jar包下的spi文件
        //加载当前工程 META-INF/services
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        URL fileUrl = contextClassLoader.getResource(BASE_PATH);
        if (ObjectUtil.isNull(fileUrl)) {
            throw new RuntimeException("Failed to load SPI resources");
        }
        File file = new File(fileUrl.getPath());
        File[] children = file.listFiles();
        if (ObjectUtil.isNotNull(children)) {
            for (File child : children) {
                String key = child.getName();
                List<String> value = getServiceImplNames(child);
                SPI_CONTENT.put(key, value);
            }
        }
    }

    private static List<String> getServiceImplNames(File file) {
        List<String> implNames = new ArrayList<>();
        try {
            FileReader fileReader = new FileReader(file);
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            while (true) {
                String line = bufferedReader.readLine();
                if (StrUtil.isBlank(line)) {
                    break;
                } else {
                    implNames.add(line);
                }
            }
        } catch (IOException e) {
            log.error("Error reading file: " + file.getPath(), e);
        }
        return implNames;
    }

    public static void main(String[] args) {

    }

    public static <T> ObjectWrapper<T> get(Class<T> clazz) {
        List<ObjectWrapper<?>> objectWrapperList = SPI_IMPLEMENT.get(clazz);
        if (!ObjectUtil.isNull(objectWrapperList) && !objectWrapperList.isEmpty()) {
            return (ObjectWrapper<T>) SPI_IMPLEMENT.get(0);
        }
        buildCache(clazz);
        return (ObjectWrapper<T>) SPI_IMPLEMENT.get(clazz).get(0);
    }

    public static <T> List<ObjectWrapper<T>> getList(Class<T> clazz) {
        List<ObjectWrapper<?>> objectWrapperList = SPI_IMPLEMENT.get(clazz);
        if (!ObjectUtil.isNull(objectWrapperList) && !objectWrapperList.isEmpty()) {
            return objectWrapperList.stream().map(objectWrapper -> (ObjectWrapper<T>) objectWrapper).collect(Collectors.toList());
        }
        buildCache(clazz);
        if (!ObjectUtil.isNull(objectWrapperList) && !objectWrapperList.isEmpty()) {
            return SPI_IMPLEMENT.get(clazz).stream().map(objectWrapper -> (ObjectWrapper<T>) objectWrapper).collect(Collectors.toList());
        }
        return null;
    }

    private synchronized static <T> void buildCache(Class<T> clazz) {
        String name = clazz.getName();
        List<ObjectWrapper<?>> list = new ArrayList<>();
        List<String> implsNames = SPI_CONTENT.get(name);
        if (ObjectUtil.isNotNull(implsNames) && !implsNames.isEmpty()) {
            try {
                for (String implsName : implsNames) {
                    String[] split = implsName.split("-");
                    if (split.length != 3) {
                        throw new IllegalArgumentException("配置文件 不合法: " + implsName);
                    }
                    Byte code = Byte.valueOf(split[0]);
                    String type = split[1];
                    String className = split[2];
                    Class<?> implsClazz = Class.forName(className);
                    T instance = (T) implsClazz.getConstructor().newInstance();
                    ObjectWrapper<T> tObjectWrapper = new ObjectWrapper<>();
                    tObjectWrapper.setObjectCode(code);
                    tObjectWrapper.setObjectType(type);
                    tObjectWrapper.setObjectImpl(instance);
                    list.add(tObjectWrapper);
                }
                SPI_IMPLEMENT.put(clazz, list);
            } catch (Exception e) {
                log.error("Error instantiating implementation: " + clazz, e);
            }
        }
        log.info("没有找到对应的spi文件");
    }
}